package application.script;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class XMLProgramBuilder {

	private File xmlFile;
	private StringBuilder builder;
	private Document dom;
	private ArrayList<Method> methods;
	
	public XMLProgramBuilder(File f) throws FileNotFoundException
	{
		if(f == null || !f.exists())
			throw new FileNotFoundException();
		
		this.xmlFile = f;
	}
	
	private void createProgram() throws InvalidSkeletonFormatException
	{
		Document d = getDoc();
		NodeList programs = d.getElementsByTagName("program");
		if(programs.getLength() == 0)
			throw new InvalidSkeletonFormatException("No PROGRAM tags found");
		Element programElement = (Element)programs.item(0);
		getBuffer().setLength(0);//reset
		getMethods().clear();
		
		String programName = programElement.getAttribute("name");
		if(programName == null)
			throw new InvalidSkeletonFormatException("No PROGRAM NAME was specified");
		else if(programName.trim().indexOf(" ") > 0)
			throw new InvalidSkeletonFormatException("Multi-word PROGRAM NAMEs are not allowed.");
		getBuffer().append(".PROGRAM " + programName.trim() + "()\n\n");
		
		NodeList variableList = programElement.getElementsByTagName("variables");
		NodeList initList = programElement.getElementsByTagName("init");
		NodeList methodList = programElement.getElementsByTagName("methods");
		
		
		if(variableList.getLength() > 0)
		{
			NodeList variables = ((Element)variableList.item(0)).getElementsByTagName("variable");
			for(int i = 0; i < variables.getLength(); i++)
			{
				Element v = (Element)variables.item(i);
				String name = v.getAttribute("name");
				if(name == null || name.trim().length() == 0)
					continue;
				getBuffer().append("\tAUTO $" + name.trim() + "\n");
			}
			getBuffer().append("\n");
		}
		
		if(initList.getLength() > 0)
		{
			Element init = (Element)initList.item(0);
			String data = init.getTextContent();
			getBuffer().append(data + "\n");
		}
		
		if(methodList.getLength() > 0)
		{
			NodeList methodElements = ((Element)methodList.item(0)).getElementsByTagName("method");
			for(int i = 0; i < methodElements.getLength(); i++)
			{
				Element m = (Element)methodElements.item(i);
				String name = m.getAttribute("name");
				if(name == null || name.trim().length() == 0)
					continue;
				String data = m.getTextContent();
				if(data == null || data.trim().length() == 0)
					continue;
				
				String desc = m.getAttribute("description");
				if(desc == null){desc = "";}
				
				Method meth = new Method();
				meth.setName(name);
				meth.setDescription(desc);
				meth.setContent(data);
				meth.setId(i+2);
				getMethods().add(meth);
			}
		}
		
		Method start = createOperator();
		getMethods().add(0, start);
		for(Method m: getMethods())
			getBuffer().append(m.toString());
		
		getBuffer().append(".END");
		mapMethods();
	}
	private Method createOperator()
	{
		Method m = new Method();
		m.setId(1);
		m.setName("start");
		m.setDescription("Entry point into the system");
		String con = 
		"\tTYPE \"Reading Serial Data\"\n" +
		"\tIF IOSTAT(slun) < 0\n\t\tWRITE(slun) \"405\"\n\tEND\n" +
		"\tREAD (slun) $text\n" +
		"\tTYPE \"Command Received: \" + $text\n" +
		"\tWRITE(slun) \"203\"\n";

		
		
		for(int i = 0; i < getMethods().size(); i++)
		{
			Method mer = getMethods().get(i);
			con += "\tIF $text == \"" + mer.getName() + "\" GOTO " + mer.getId() + "\n";
		}
		
		con += "\tWRITE(slun) \"402\"\n"+
		"\tGOTO exit\n";
		m.setContent(con);
		
		return m;
	}
	private List<Method> getMethods()
	{
		if(methods == null)
			methods = new ArrayList<Method>();
		return methods;
	}
	private void mapMethods()
	{
		String content = getBuffer().toString();
		for(Method m : getMethods())
		{
			content = content.replaceAll("GOTO " +  m.getName(), "GOTO " + m.getId());
			
		}
		getBuffer().setLength(0);
		getBuffer().append(content);
	}
	private File getFile()
	{
		return this.xmlFile;
	}
	private Document createDOM()
	{
		try{
		DocumentBuilder build = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		return build.parse(getFile());
		}catch(Exception e){return null;}
	}
	private Document getDoc()
	{
		if(dom == null)
			dom = createDOM();
		return dom;
	}
	private StringBuilder getBuffer()
	{
		if(builder == null)
			builder = new StringBuilder();
		return builder;
	}
	public String getProgramString() throws InvalidSkeletonFormatException
	{
		createProgram();
		return getBuffer().toString();
	}
}
